from bson.objectid import ObjectId
from pymongo import MongoClient
from Cache import *

import sys
import re
import json
import gensim
import random
import difflib
import datetime
import jieba.posseg as pseg

sys.path.append("../")

from config import *


def cos(vector1, vector2):
    dot_product = 0.0
    normA = 0.0
    normB = 0.0

    for a, b in zip(vector1, vector2):
        dot_product += a * b
        normA += a ** 2
        normB += b ** 2

    if normA == 0.0 or normB == 0.0:
        return None
    else:
        return dot_product / ((normA * normB) ** 0.5)


def get_model_vector(query):
    return model.infer_vector([word for word, flag in pseg.cut(query) if len(word) > 1 and word not in stoplist and flag not in ['x', 'm']])


def cos_similarity_calculation(query0, query1):
    if cache.get('cos_similarity_calculation', query0 + '-' + query1):
        return cache.get('cos_similarity_calculation', query0 + '-' + query1)

    scores = []

    for i in range(10):
        scores.append(cos(get_model_vector(query0), get_model_vector(query1)))

    scores.sort(key = lambda i: i)
    scores = scores[1: -1]

    score = sum(scores) / len(scores)

    cache('cos_similarity_calculation', query0 + '-' + query1, score)

    return score


def match_count(query, sim):
    query = query.lower()

    n = 0

    for i in sim:
        if query in i[1].lower():
            n += 1

    return n


def match_similar(query):
    # clu_result.sort(key = lambda i: match_count(query, i['sim']), reverse = True)
    #
    # names = {}
    # comps = {}
    #
    # for item in clu_result[:3]:
    #     # random.shuffle(item['sim'])
    #
    #     for s in item['sim']:
    #         names[s[1]] = 1
    #         comps[s[0]] = 1
    #
    # return names, comps

    for sw in synonymous_words:
        for w in sw:
            if query in w or w in query:
                return sw

    return []


# def seq_similarity_calculation(query, word):
#     query, word = query.lower(), word.lower()
#
#     if query in similar_map:
#         for w in similar_map[query]['words']:
#             if w in word:
#                 if similar_map[query]['relation'] == 'contain':
#                     return 1 + random.random()
#                 elif similar_map[query]['relation'] == 'synonymous':
#                     return 1
#
#     if query in word or word in query:
#         return 1
#
#     return difflib.SequenceMatcher(None, query, word).quick_ratio()


# def sematic_similarity_calculation(query, word):
#     return cos_similarity_calculation(query, word) + difflib.SequenceMatcher(None, query, word).quick_ratio()


# def seq_similarity_score(word0, word1):
#     word0, word1 = word0.lower(), word1.lower()
#
#     if word0 == word1:
#         return 1
#
#     return 0


def filter_job(jd):
    # 带有比较低端的关键字的过滤掉
    for sw in shielding_words:
        if sw in jd['name']:
            return True

    # 学历太低的过滤掉
    if jd['edu'] in ['大专', '中专', '高中']:
        return True

    desc = jd['translated_desc'] if 'translated_desc' in jd else jd['description']

    if exp_require(desc):
        return True

    return False


rank_info = {}


def job_rank(jd):
    compname = re.sub('有限公司.*', '', jd['company'])
    score = 0

    # 目标企业加分
    if is_target_enterprise(compname) and not compname in rank_info:
        score += 5
        rank_info[compname] = True

    # 热门企业加分
    elif is_hot_enterprise(compname) and not compname in rank_info:
        score += 4
        rank_info[compname] = True

    # 针对校园、留学生的职位加分
    if fit_for_student(jd):
        score += 1

    # 英文职位
    if english_need(jd):
        score += 1

    # 如果是高级职位则统一往后排
    if senior_job(jd['name']):
        score -= 30

    # 把自导入jd往前放
    priority = jd['priority'] if 'priority' in jd else 0

    return score + priority + random.random()


def fit_for_student(jd):
    if not jd:
        return False

    name = jd['translated_name'] if 'translated_name' in jd else jd['name']

    for k in ['2018', '18届', '2019', '19届', '留学', '海归', '校招', '校园', '实习', '见习', '春招', '夏招', '秋招', '冬招', '专训生', '海外', '学生', '应届']:
        if name.find(k) != -1:
            return True

    return False


def english_need(jd):
    if 'translated_name' in jd or 'translated_desc' in jd:
        return True

    if '英文' in jd['name'] or '英语' in jd['name']:
        return True

    if '雅思' in jd['description'] or '托福' in jd['description'] or '英语流利' in jd['description']:
        return True

    return False


def senior_job(name):
    for i in ['总监', '主管', '主任', '经理', '部长', '队长']:
        if name.find(i) != -1 and name.find('助理') == -1:
            return True

    return False


def is_hot_enterprise(name):
    if cache.get('is_hot_enterprise', name):
        return cache.get('is_hot_enterprise', name)

    for i in hot_enterprise:
        if i in name or name in i:
            cache('is_hot_enterprise', name, True)
            return True

    cache('is_hot_enterprise', name, False)
    return False


def is_target_enterprise(name):
    if cache.get('is_target_enterprise', name):
        return cache.get('is_target_enterprise', name)

    for i in target_enterprise:
        if i in name or name in i:
            cache('is_target_enterprise', name, True)
            return True

    cache('is_target_enterprise', name, False)
    return False


def parse_form_data(form):
    data = None

    for k in form.to_dict():
        data = json.loads(k)

    return data


def area_match(citys, query):
    for c in citys:
        if c in query:
            return True

    return False


def industry_match(Industry, industry):
    for i in industry_map[Industry]:
        if i in industry:
            return True

    return False


# 统计id的收藏人数
def collection_number(platform, id):
    num = 0

    for user in user_log.find():
        for item in user['job_collected']:
            if item['platform'] == platform and item['id'] == id:
                num += 1

    return num


# 为jd对象添加企业logo
def append_logo_url(jd):
    # 已经有logoUrl
    if 'logoUrl' in jd and jd['logoUrl']:
        return

    if not 'platform' in jd or not jd['platform'] in corps_map:
        return

    name = jd['company'].replace('拉勾未认证企业', '').strip()

    if not name:
        return

    company = corps_map[jd['platform']].find_one({'name': name})

    if company:
        if 'logoUrl' in company:
            jd['logoUrl'] = company['logoUrl']
        elif 'logo_url' in company:
            jd['logoUrl'] = company['logo_url']
        else:
            jd['logoUrl'] = ''
    else:
        jd['logoUrl'] = ''

    if jd['logoUrl'].startswith('//'):
        jd['logoUrl'] = 'http:' + jd['logoUrl']
    elif jd['logoUrl'].startswith('www'):
        jd['logoUrl'] = 'http://' + jd['logoUrl']


def time():
    return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')


def default_search(Area = '', PageIndex = 0, PageSize = 6, BeginPay = 6000, EndPay = 30000, Industry = '', UserID = '', UserKey = ''):
    """
    默认搜索
    :param Area:
    :param PageIndex:
    :param PageSize:
    :param BeginPay:
    :param EndPay:
    :param Industry:
    :return:
    """
    result = []

    # 优先使用缓存
    if cache.get('default_search', (Area, PageIndex, PageSize, BeginPay, EndPay, Industry)):
        result = cache.get('default_search', (Area, PageIndex, PageSize, BeginPay, EndPay, Industry))
        return random.sample(result, PageSize), len(result)

    if Area:
        citys = prov_city.parse(Area)

    for name, value in postition_memory_db.items():
        for company, value in value.items():
            if not is_hot_enterprise(company):
                continue

            for location, value in value.items():
                if Area and not area_match(citys, location):
                    continue

                for industry, value in value.items():
                    if Industry and Industry in industry_map and not industry_match(Industry, industry):
                        continue

                    for salary, value in value.items():
                        if isinstance(salary, tuple) and len(salary) and (BeginPay >= int(max(salary)) or EndPay <= int(min(salary))):
                            continue

                        # for item in value:
                        #     if item['platform'] != 'boss':
                        #         continue
                        #
                        #     result.append(item)
                        result += value

    random.shuffle(result)

    # 过滤带有屏蔽关键字的职位
    result = list(filter(lambda i: not filter_job(i), result))

    # 职位排名
    result.sort(key = job_rank, reverse = True)
    rank_info = {}

    # 缓存结果
    cache('default_search', (Area, PageIndex, PageSize, BeginPay, EndPay, Industry), result)

    return random.sample(result, PageSize), len(result)


def company_search(Title = '', Area = '', PageIndex = 0, PageSize = 10, BeginPay = 6000, EndPay = 20000, Industry = ''):
    """
    企业关键字搜索
    :param Title:
    :param Area:
    :param PageIndex:
    :param PageSize:
    :param BeginPay:
    :param EndPay:
    :param Industry:
    :return:
    """
    result = []

    query = Title.lower()

    if Area:
        citys = prov_city.parse(Area)

    for name, value in postition_memory_db.items():
        for company, value in value.items():
            if company.find(query) == -1:
                continue

            for location, value in value.items():
                if Area and not area_match(citys, location):
                    continue

                for industry, value in value.items():
                    if Industry and Industry in industry_map and not industry_match(Industry, industry):
                        continue

                    for salary, value in value.items():
                        if len(salary) and (BeginPay >= max(salary) or EndPay <= min(salary)):
                            continue

                        # 网站上出现的职位最低薪水都要高于6000
                        if len(salary) and max(salary) <= 6000:
                            continue

                        result += value

    # result.sort(key = lambda i: seq_similarity_calculation(query, i['name']), reverse = True)

    # 过滤带有屏蔽关键字的职位
    result = list(filter(lambda i: not filter_job(i), result))

    # 职位排名
    result.sort(key = job_rank, reverse = True)

    rank_info = {}

    return result[PageIndex * PageSize: (PageIndex + 1) * PageSize], len(result)


def name_match(name, names_similar):
    if isinstance(names_similar, list):
        for i in names_similar:
            if name in i or i in name:
                return True

        return False
    else:
        if name in names_similar or names_similar in name:
            return True
        else:
            return False


def semantics_search(Title = '', Area = '', PageIndex = 0, PageSize = 10, BeginPay = 6000, EndPay = 20000, Industry = ''):
    """
    语义搜索
    :param Title:
    :param Area:
    :param PageIndex:
    :param PageSize:
    :param BeginPay:
    :param EndPay:
    :param Industry:
    :param st:
    :return:
    """
    result = []

    # 优先使用缓存
    if cache.get('semantics_search', (Title, Area, BeginPay, EndPay, Industry)):
        result = cache.get('semantics_search', (Title, Area, BeginPay, EndPay, Industry))
        return result[PageIndex * PageSize: (PageIndex + 1) * PageSize], len(result)

    query = Title.lower()

    if Area:
        citys = prov_city.parse(Area)

    # 查询关键字映射，为了有更好的搜索结果
    if query and query in query_map:
        query = query_map[query]

    # 根据查询词匹配语义最相关的词
    # if query:
    #     names_similar, comps_similar = match_similar(query)
    # else:
    #     names_similar, comps_similar = {}, {}

    # 如果用户输入的是一些范围很广的查询词，则查询词映射到一系列被这个词包含的词
    if query in similar_map:
        names_similar = similar_map[query]['words']
    else:
        names_similar = match_similar(query)

    for name, value in postition_memory_db.items():
        if names_similar and not name_match(name, names_similar):
            continue

        if not names_similar and not name_match(name, query):
            continue

        for company, value in value.items():
            # if comps_similar and not company in comps_similar:
            #     continue

            for location, value in value.items():
                if Area and not area_match(citys, location):
                    continue

                for industry, value in value.items():
                    if Industry and Industry in industry_map and not industry_match(Industry, industry):
                        continue

                    for salary, value in value.items():
                        if len(salary) and (BeginPay >= max(salary) or EndPay <= min(salary)):
                            continue

                        # 网站上出现的职位最低薪水都要高于6000
                        if len(salary) and max(salary) <= 6000:
                            continue

                        result += value

    # 与查询关键字字面越相似越往前排
    random.shuffle(result)

    # 过滤带有屏蔽关键字的职位
    result = list(filter(lambda i: not filter_job(i), result))

    # 职位排名
    result.sort(key = job_rank, reverse = True)

    # 缓存结果
    cache('semantics_search', (Title, Area, BeginPay, EndPay, Industry), result)

    rank_info = {}

    return result[PageIndex * PageSize: (PageIndex + 1) * PageSize], len(result)
